package cn.mw.cmdb.entity;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.mw.cmdb.enums.AssetsDiscoverScanIpTypeEnum;
import cn.mw.cmdb.exception.InputRequireException;
import cn.mw.cmdb.param.AssetsDiscoverRuleParam;
import cn.mw.cmdb.param.MwDiscoveryExecutorTypeEnum;
import cn.mw.cmdb.param.MwScanTypeDiscoveryEnum;
import cn.mw.cmdb.service.ServiceChoose;
import cn.mw.cmdb.service.impl.AssetsDiscoverServiceImpl;
import cn.mw.cmdb.util.DataCreateInfo;
import cn.mw.components.mongodb.anno.AutoId;
import cn.mw.components.mongodb.anno.CMDBProperty;
import cn.mw.components.mongodb.exception.BusinessException;
import cn.mw.microMonitorCommon.api.scan.IPRangeParam;
import cn.mw.microMonitorCommon.api.scan.SubnetParam;
import cn.mw.microMonitorCommon.enums.RuleType;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;
import java.util.List;

import static cn.mw.cmdb.param.MwScanTypeDiscoveryEnum.manualScan;

@EqualsAndHashCode(callSuper = true)
@Data
@Document("mw_cmdbmd_assets_discover")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class AssetsDiscoverInfo extends DataAuth implements ServiceChoose, DataCreateInfo, Serializable {

    @AutoId
    @CMDBProperty(searchType = PropertyTypeString.ID, searchPrefix = "_")
    private String id;

    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    @Schema(description = "扫描规则名称")
    @NotBlank
    @Size(min = 1, max = 50, message = "名称长度不能超过50个字符")
    private String ruleName;

    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    @Schema(description = "扫描类型")
    private MwScanTypeDiscoveryEnum scanType = manualScan;

    //扫描类型为定时扫描时，关联计划必填
    @Schema(description = "关联时间计划")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    private String timePlanId;

    // 执行模式
    @Schema(description = "执行模式")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    private MwDiscoveryExecutorTypeEnum executorType;

    // 规则
    @Schema(description = "规则id")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    private String customRuleId;


    @CMDBProperty(searchType = PropertyTypeInt.ID)
    @Schema(description = "扫描结果最大批次号")
    private Integer maxBatchNo;

    // 立即执行
    @CMDBProperty(searchType = PropertyTypeString.ID)
    @Schema(description = "立即执行")
    private Boolean executorNow;

    @CMDBProperty(searchType = PropertyTypeString.ID)
    @Schema(description = "执行人")
    private String executor;

    // 开始时间
    @CMDBProperty(searchType = PropertyTypeLong.ID)
    @Schema(description = "开始时间")
    private Long scanStartDate;

    // 结束时间
    @CMDBProperty(searchType = PropertyTypeLong.ID)
    @Schema(description = "结束时间")
    private Long scanEndDate;

    //代理类型
    @CMDBProperty(searchType = PropertyTypeInt.ID)
    @Schema(description = "代理类型")
    private Integer proxyType;

    // 轮训引擎ID
    @CMDBProperty(searchType = PropertyTypeString.ID)
    @Schema(description = "轮训引擎ID")
    private String engineId;

    // IP范围
    @CMDBProperty(searchType = PropertyTypeList.ID)
    @Schema(description = "IP范围")
    private List<IPRangeParam> ipRange;

    // ip地址段
    @CMDBProperty(searchType = PropertyTypeList.ID)
    @Schema(description = "ip地址段")
    private List<SubnetParam> ipSubnet;
    // ip清单
    @CMDBProperty(searchType = PropertyTypeString.ID)
    @Schema(description = "ip清单")
    private String ips;

    @CMDBProperty(searchType = PropertyTypeList.ID)
    @Schema(description = "ip类型,本次存储的IP类型")
    private List<String> ipTypeList;

    @Schema(description = "ip清单是否为ipv6")
    @CMDBProperty(searchType = PropertyTypeString.ID)
    private Boolean ipv6checked;

    // 扫描检查规则
    @CMDBProperty(searchType = PropertyTypeList.ID)
    @Schema(description = "扫描检查规则")
    private List<AssetsDiscoverRuleParam> scanRules;

    // singleton, batch,目前只有batch类型
    @CMDBProperty(searchType = PropertyTypeString.ID)
    private String taskType = "batch";

    // 当前扫描进度,batch类型才有
    @CMDBProperty(searchType = PropertyTypeInt.ID)
    private Integer progress;

    // 执行状态
    @CMDBProperty(searchType = PropertyTypeString.ID)
    private String status;

    @CMDBProperty(searchType = PropertyTypeList.ID)
    @Schema(description = "责任人")
    private List<Integer> principal;

    @Schema(description = "扫描请求超时时间,毫秒")
    @CMDBProperty
    private Integer reqTimeout;

    @Schema(description = "扫描请求重试次数")
    @CMDBProperty
    private Integer reqRetryNum;

    @Override
    public String getServiceId() {
        return AssetsDiscoverServiceImpl.ID;
    }

    public void initRuleParam() {
        if (StrUtil.isEmpty(getRuleName())) {
            throw new InputRequireException(new Object[]{getRuleName()});
        }
        if (CollUtil.isEmpty(getScanRules())) {
            throw new InputRequireException(new Object[]{getScanRules()});
        }
        if (CollUtil.isEmpty(getIpTypeList())) {
            throw new InputRequireException(new Object[]{getIpTypeList()});
        }
        if (this.scanRules != null) {
            for (AssetsDiscoverRuleParam scanRule : this.scanRules) {
                if (scanRule == null || scanRule.getProtoType() == null) {
                    throw new BusinessException("proto-type-null");
                }
                if (RuleType.ICMP.getName().equals(scanRule.getProtoType()) && StrUtil.isBlank(scanRule.getPort())) {
                    scanRule.setPort(RuleType.ICMP.getPort());
                } else if (RuleType.SNMP.getName().equals(scanRule.getProtoType()) && StrUtil.isBlank(scanRule.getPort())) {
                    scanRule.setPort(RuleType.SNMPv1.getPort());
                } else if (RuleType.Agent.getName().equals(scanRule.getProtoType()) && StrUtil.isBlank(scanRule.getPort())) {
                    scanRule.setPort(RuleType.Agent.getPort());
                }
            }
        }
        if (this.getIpTypeList() != null) {
            BusinessException businessException = new BusinessException("invalid-address");

            for (String ipType : this.getIpTypeList()) {
                AssetsDiscoverScanIpTypeEnum ipTypeEnum = AssetsDiscoverScanIpTypeEnum.valueOf(ipType);
                switch (ipTypeEnum) {
                    case ips -> {
                        ips = ips.replaceAll(" ", "");
                        AssetsDiscoverScanIpTypeEnum.validIps(ips, ipv6checked, businessException);
                    }
                    case ipRange -> {
                        for (IPRangeParam ipRangeParam : ipRange) {
                            ipRangeParam.setStartip(ipRangeParam.getStartip().trim());
                            ipRangeParam.setEndip(ipRangeParam.getEndip().trim());
                        }
                        AssetsDiscoverScanIpTypeEnum.validIpRange(ipRange, businessException);
                    }
                    case ipSubnet -> {
                        for (SubnetParam subnetParam : ipSubnet) {
                            subnetParam.setSubnet(subnetParam.getSubnet().trim());
                        }
                        AssetsDiscoverScanIpTypeEnum.validIpSubnet(ipSubnet, businessException);
                    }
                }
            }
        }
    }
}
